package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path"
	"strings"

	"github.com/fkxxyz/go-dir-header/dirHeader"
	"github.com/gin-gonic/gin"
)

// 支持启动时显示构建日期和构建版本
// 需要通过命令 ` go build -ldflags "-X main.build=`git rev-parse HEAD`" ` 打包
var build = "not set"

type dist struct {
	prefix string
}

func newDist(prefix string) *dist {
	return &dist{
		prefix: prefix,
	}
}

func resolveOsError(c *gin.Context, err error) {
	if os.IsNotExist(err) {
		c.String(http.StatusNotFound, err.Error())
	} else if os.IsPermission(err) {
		c.String(http.StatusForbidden, err.Error())
	} else {
		c.String(http.StatusInternalServerError, err.Error())
	}
}

func (d *dist) handleGET(c *gin.Context) {
	if c.Request.Method != "GET" {
		return
	}
	w := c.Writer
	r := c.Request
	uPath := r.URL.Path
	if !strings.HasPrefix(uPath, "/") {
		uPath = "/" + uPath
		r.URL.Path = uPath
	}
	uPath = path.Clean(uPath)

	fullPath := path.Join(d.prefix, uPath)
	file, err := os.Open(fullPath)
	if err != nil {
		resolveOsError(c, err)
		c.Abort()
		return
	}
	defer func(file *os.File) {
		_ = file.Close()
	}(file)

	info, err := file.Stat()
	if err != nil {
		resolveOsError(c, err)
		c.Abort()
		return
	}

	if info.IsDir() {
		dirHeader.DirList(w, fullPath, uPath)
		c.AbortWithStatus(http.StatusOK)
		return
	}

	http.ServeContent(w, r, info.Name(), info.ModTime(), file)
}

func (d *dist) handlePOST(c *gin.Context) {
	name := c.Query("name")
	if len(name) == 0 {
		err := fmt.Errorf("no name set")
		c.String(http.StatusBadRequest, err.Error())
		return
	}
	destDir := path.Join(d.prefix, name)
	astaFile := path.Join(d.prefix, name+".asta")
	err := os.RemoveAll(destDir)
	if err != nil {
		resolveOsError(c, err)
		return
	}
	rawData, err := c.GetRawData()
	if err != nil {
		c.String(http.StatusBadRequest, err.Error())
		return
	}
	err = ioutil.WriteFile(astaFile, rawData, 0o644)
	if err != nil {
		resolveOsError(c, err)
		return
	}
	cmd := exec.Command(
		"/usr/lib/astah_professional/astah-command.sh",
		"-f", astaFile,
		"-image", "all",
		"-t", "svg",
		"-o", d.prefix,
	)
	var out bytes.Buffer
	cmd.Stdout = &out
	err = cmd.Run()
	if err != nil {
		msg := fmt.Sprintf("%s\n%s", err.Error(), out.String())
		c.String(http.StatusBadRequest, msg)
		return
	}
	c.Status(http.StatusOK)
}

func (d *dist) handleDELETE(c *gin.Context) {
	name := c.Query("name")
	if len(name) == 0 {
		err := fmt.Errorf("no name set")
		c.String(http.StatusBadRequest, err.Error())
		return
	}
	destDir := path.Join(d.prefix, name)
	astaFile := path.Join(d.prefix, name+".asta")
	err := os.Remove(astaFile)
	if err != nil {
		if err != nil {
			resolveOsError(c, err)
			return
		}
	}
	err = os.RemoveAll(destDir)
	if err != nil {
		if err != nil {
			resolveOsError(c, err)
			return
		}
	}
	c.Status(http.StatusOK)
}

func main() {
	bind := flag.String("bind", "0.0.0.0:8080", "listen address")
	root := flag.String("root", "/tmp/astah", "root directory")
	flag.Parse()
	fmt.Printf("Build: %s\n", build)

	err := os.MkdirAll(*root, 0o755)
	if err != nil {
		_, _ = fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	d := newDist(*root)

	r := gin.Default()
	r.Use(d.handleGET)
	r.POST("/", d.handlePOST)
	r.DELETE("/", d.handleDELETE)
	err = r.Run(*bind)
	if err != nil {
		_, _ = fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
